package com.zjmzxfzhl.integrityconstraint.service.impl;

import com.zjmzxfzhl.integrityconstraint.annotation.SpareImplAnnotation;
import com.zjmzxfzhl.integrityconstraint.config.IntegrityConstraintProperties;
import com.zjmzxfzhl.integrityconstraint.exception.IntegrityConstraintIgnoreException;
import com.zjmzxfzhl.integrityconstraint.pojo.ToCheck;
import com.zjmzxfzhl.integrityconstraint.service.AbstractIntegrityConstraintService;
import com.zjmzxfzhl.integrityconstraint.entity.IntegrityConstraint;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis作为缓存的业务完整性验证实现类
 *
 * @author 庄金明
 */
@SpareImplAnnotation(config = "@integrityConstraintProperties.primaryCircuitBreakerConfig",
        spareBeanId = "@integrityConstraintProperties.spareIntegrityConstraintService")
/// 以下方式也可以
/// @SpareImplAnnotation(config = "${integrityConstraint.properties.primaryCircuitBreakerConfig}",
///         spareBeanId = "${integrityConstraint.properties.spareIntegrityConstraintService}")
public class RedisIntegrityConstraintService extends AbstractIntegrityConstraintService {
    private final RedisTemplate redisTemplate;

    public RedisIntegrityConstraintService(RedisTemplate redisTemplate, IntegrityConstraintProperties integrityConstraintProperties) {
        super(integrityConstraintProperties);
        this.redisTemplate = redisTemplate;
    }

    @Override
    public Boolean save(IntegrityConstraint integrityConstraint) {
        try {
            Long expire = redisTemplate.getExpire(integrityConstraint.getBusinessKey(), TimeUnit.MILLISECONDS);
            expire = expire > integrityConstraintProperties.getExpire() * 1000 ? expire : integrityConstraintProperties.getExpire() * 1000;
            expire = expire > integrityConstraint.getExpire() ? expire : integrityConstraint.getExpire();
            redisTemplate.boundHashOps(integrityConstraint.getBusinessKey()).put(integrityConstraint.getVerifyType(), integrityConstraint);
            redisTemplate.expire(integrityConstraint.getBusinessKey(), expire, TimeUnit.MILLISECONDS);
            return true;
        }
        // redis连接异常时，记录次数，否则不记录次数
        catch (RedisConnectionFailureException e) {
            throw e;
        } catch (Exception e) {
            throw new IntegrityConstraintIgnoreException("IntegrityConstraint save error by redis", e);
        }
    }

    @Override
    public Boolean check(String businessKey, List<ToCheck> toChecks) {
        List<IntegrityConstraint> integrityConstraints = this.getList(businessKey);
        return this.commonCheck(integrityConstraints, toChecks);
    }

    @Override
    public Boolean delete(String businessKey) {
        try {
            redisTemplate.delete(businessKey);
            return true;
        }
        // redis连接异常时，记录次数，否则不记录次数
        catch (RedisConnectionFailureException e) {
            throw e;
        } catch (Exception e) {
            throw new IntegrityConstraintIgnoreException("IntegrityConstraint delete error by redis", e);
        }
    }

    protected List<IntegrityConstraint> getList(String businessKey) {
        List<IntegrityConstraint> integrityConstraints = null;
        try {
            integrityConstraints = redisTemplate.boundHashOps(businessKey).values();
            integrityConstraints = integrityConstraints
                    .stream()
                    .sorted(Comparator.comparingLong(IntegrityConstraint::getCreateTime))
                    .collect(Collectors.toList());
        }
        // redis连接异常时，记录次数，否则不记录次数
        catch (RedisConnectionFailureException e) {
            throw e;
        } catch (Exception e) {
            throw new IntegrityConstraintIgnoreException("IntegrityConstraint check error by redis", e);
        }
        return integrityConstraints;
    }
}
